#include "stdafx.h"
#include "Camera.h"
#include "..\EdgeControls\Input.h"
#include "..\EdgeMath\Math.h"
#include "..\EdgeMath\Vectors.h"
#include "..\EdgeControls\keys.h"
#include "..\EdgeControls\namedKey.h"
#include "..\EdgeControls\Controls.h"
#include "..\EdgeMath\VectorHelper.h"
#include "..\EdgeGameCore\Functor.h"
#include "..\EdgeGameCore\Objects\GameObject.h"
#include <Windows.h>

using namespace VectorHelper;

/*static*/ Camera* Camera::instance = nullptr;

void mouse_move(void**);
void scrollWeel_up(void**);
void scrollWeel_down(void**);


Camera::Camera(int viewport_width, int viewport_height) : move_speed(1.f)
{ 
	this->viewport_width = viewport_width;
	this->viewport_height = viewport_height;

	position = vector_3::right;

	Input* pInput = Input::GetInstance();
	pInput->mouse_moved->Subscribe(Functor<mouse_event_handler>(&mouse_move, 0));
	pInput->scrollWeel_up->Subscribe(Functor<mouse_event_handler>(&scrollWeel_up, 0));
	pInput->scrollWeel_down->Subscribe(Functor<mouse_event_handler>(&scrollWeel_down, 0));	
}

void mouse_move(void** params)
{
	long* iParams = (long*)(params);
	long delta_x = iParams[0];
	long delta_y = iParams[1];

	Camera* pCamera = Camera::GetInstance();
	Vector3 camLook = pCamera->getLookAt();

	Vector3 camPosition = pCamera->getPosition();
	

	// orthonormalize to get up and left vectors
	
	Vector3 to = camPosition;
	to.Normalize();
	Vector3 right = to ^ vector_3::up;
	right.Normalize();
	Vector3 up = to ^ right;
	up.Normalize();

	float rot_yaw = Math::to_radians((float)delta_x);
	float rot_pitch =  Math::to_radians((float)delta_y);

	camPosition += (up * pCamera->getMoveSpeed()) * rot_pitch;
	camPosition += (right * pCamera->getMoveSpeed()) * rot_yaw;

	camPosition.Normalize();

	Camera::GetInstance()->setPosition(camPosition);

}

void scrollWeel_up(void** params)
{
	Camera::GetInstance()->setTargetRange(Camera::GetInstance()->getTargetRange() + 1.f);
}

void scrollWeel_down(void** params)
{
	Camera::GetInstance()->setTargetRange(Camera::GetInstance()->getTargetRange() - 1);
}

/*static*/ void Camera::ShutDown(void)
{
	delete instance;
}

Vector3 Camera::getUpVector(void)
{
	return getRightVector() ^ getForwardVector();
}

Camera::~Camera(void)
{

}

Vector3 Camera::getWorldPosition(void) const
{
	Vector3 camPosition = position;
	camPosition.Normalize();

	Vector3 targetPosition = target;

	return targetPosition + camPosition * target_range;
}

void Camera::Update(float dt)
{
	lookat = target;	
}

Vector3 Camera::getForwardVector(void) 
{
	Vector3 outval = position;
	outval.Normalize();
	return outval;
}


Vector3 Camera::getRightVector(void)
{
	Vector3 outval = getForwardVector() ^ VectorHelper::vector_3::up;
	outval.Normalize();
	return outval;
}

Matrix Camera::getWorld(void)
{
	return VectorHelper::Matricies::Identity;
}

Matrix Camera::getView(void)
{
	return VectorHelper::Matricies::CreateLookAt(getWorldPosition(), lookat, VectorHelper::vector_3::up);
}

Matrix Camera::getProj(void)
{
	return VectorHelper::Matricies::CreatePerspectiveFieldOfView(Math::to_radians(90.f), viewport_width / (float)viewport_height, nearPlaneDistance, farPlaneDistance);
}

/*static*/ void Camera::Inilitize(int backbuffer_width, int backbuffer_height)
{
	instance = new Camera(backbuffer_width, backbuffer_height);
}